using System;
using System.Collections.Generic;

namespace Common
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Range<T> : IComparable<Range<T>>  
        where T : IComparable
    {
        /// <summary>
        /// 
        /// </summary>
        public T FromValue;
        /// <summary>
        /// 
        /// </summary>
        public T ToValue;

        /// <summary>
        /// 
        /// </summary>
        public Range()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromValue"></param>
        /// <param name="toValue"></param>
        public Range(T fromValue, T toValue)
        {
            this.FromValue = fromValue;
            this.ToValue = toValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="otherRange"></param>
        /// <returns></returns>
        public Range<T> Union(Range<T> otherRange)
        {
            Range<T> result = new Range<T>();
            result.FromValue = (this.FromValue.CompareTo(otherRange.FromValue) > 0
                                    ? otherRange.FromValue
                                    : this.FromValue);
            result.ToValue = (this.ToValue.CompareTo(otherRange.ToValue) > 0 ? this.ToValue : otherRange.ToValue);
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="otherRange"></param>
        /// <returns></returns>
        public Range<T> Intersection(Range<T> otherRange)
        {
            if(this.FromValue.CompareTo(otherRange.FromValue)<0 )
            {
                if(this.ToValue.CompareTo(otherRange.FromValue)>0)
                {
                    Range<T> result = new Range<T>();
                    result.FromValue = otherRange.FromValue;
                    result.ToValue = (this.ToValue.CompareTo(otherRange.ToValue) > 0 ? otherRange.ToValue : this.ToValue);
                    return result;
                }
            }
            else
            {
                if(otherRange.ToValue.CompareTo(this.FromValue)>0)
                {
                    Range<T> result = new Range<T>();
                    result.FromValue = this.FromValue;
                    result.ToValue = (this.ToValue.CompareTo(otherRange.ToValue) > 0 ? otherRange.ToValue : this.ToValue);
                    return result;
                }
            }
            return Range<T>.EmptyRange;
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                if (this.ToValue.CompareTo(this.FromValue) > 0)
                    return false;
                else
                    return true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public static Range<T> EmptyRange
        {
            get
            {
                Range<T> result = new Range<T>();
                result.FromValue = result.ToValue = default(T);
                return result;
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="otherRange"></param>
        /// <returns></returns>
        public bool OverlapWith(Range<T> otherRange)
        {
            if (this.FromValue.CompareTo(otherRange.FromValue) < 0 )
            {
                if(this.ToValue.CompareTo(otherRange.FromValue) > 0)
                {
                    return true;                    
                }
            }
            else
            {
                if (otherRange.ToValue.CompareTo(this.FromValue) > 0)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="range"></param>
        /// <returns></returns>
        public bool Contains(Range<T> range)
        {
            if (this.FromValue.CompareTo(range.FromValue) <= 0 &&
                this.ToValue.CompareTo(range.ToValue) >= 0)
                return true;
            else
                return false;
        }

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: 
        ///                     Value 
        ///                     Meaning 
        ///                     Less than zero 
        ///                     This object is less than the <paramref name="other"/> parameter.
        ///                     Zero 
        ///                     This object is equal to <paramref name="other"/>. 
        ///                     Greater than zero 
        ///                     This object is greater than <paramref name="other"/>. 
        /// </returns>
        /// <param name="other">An object to compare with this object.
        ///                 </param>
        public int CompareTo(Range<T> other)
        {
            if (this.FromValue.CompareTo(other.FromValue) == 0)
                return this.ToValue.CompareTo(other.ToValue);
            else
                return this.FromValue.CompareTo(other.FromValue);
        }

        public override string ToString()
        {
            return string.Format("{0}-{1}", this.FromValue, this.ToValue);
        }

        public List<Range<T>> Remove(List<Range<T>> ranges)
        {
            List<Range<T>> remains=new List<Range<T>>();
            remains.Add(this);
            foreach(Range<T> range in ranges)
            {
                List<Range<T>> toRemoves = new List<Range<T>>();
                List<Range<T>> toAdds = new List<Range<T>>();
                foreach(Range<T> remain in remains)
                {
                    if(remain.OverlapWith(range))
                    {
                        if(remain.FromValue.CompareTo(range.FromValue)<0
                            && remain.ToValue.CompareTo(range.ToValue)>0)
                        {
                            if (!toRemoves.Contains(remain))
                                toRemoves.Add(remain);
                            toAdds.Add(new Range<T>(remain.FromValue, range.FromValue));
                            toAdds.Add(new Range<T>(range.ToValue, remain.ToValue));
                        }
                        else if(remain.FromValue.CompareTo(range.FromValue)>0 && 
                            remain.ToValue.CompareTo(range.ToValue)<0)
                        {
                            if (!toRemoves.Contains(remain))
                                toRemoves.Add(remain);
                        }
                        else if (remain.FromValue.CompareTo(range.FromValue) < 0 &&
                            remain.ToValue.CompareTo(range.ToValue) < 0)
                        {
                            if (!toRemoves.Contains(remain))
                                toRemoves.Add(remain);
                            toAdds.Add(new Range<T>(remain.FromValue,range.FromValue));
                        }
                        else if (remain.FromValue.CompareTo(range.FromValue) < 0 &&
                            remain.ToValue.CompareTo(range.ToValue) < 0)
                        {
                            if (!toRemoves.Contains(remain))
                                toRemoves.Add(remain);
                            toAdds.Add(new Range<T>(range.ToValue, remain.ToValue));
                        }
                        break;
                    }
                }

                if(toRemoves.Count>0)
                {
                    foreach (Range<T> toRemove in toRemoves)
                        remains.Remove(toRemove);
                }
                if(toAdds.Count>0)
                {
                    foreach(Range<T> toAdd in toAdds)
                        remains.Add(toAdd);
                }
            }

            return remains;
        }

        public override int GetHashCode()
        {
            return this.FromValue.GetHashCode() + this.ToValue.GetHashCode();
        }

        

    }
}
